class MagicCube{
    constructor(param){

        this.scene = param.scene;
        this.blocks = [];
        this.initBlock();
    }

    initBlock(){
        for(let x=-1; x<=1; x++){
            for(let y=-1; y<=1; y++){
                for(let z=-1; z<=1; z++){
                    let block = new Block({
                        x: x,
                        y: y,
                        z: z
                    });
                    this.blocks.push(block);
                    this.scene.add(block.mesh);
                }
            }
        }
    }

    nearTo(a,b) {
        if(Math.abs(a-b)<0.1){
            return true;
        }
        return false;
    }

    fixPosition(pos){
        pos.x = Math.round(pos.x);
        pos.y = Math.round(pos.y);
        pos.z = Math.round(pos.z);
    }

    R(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.x,1)){
                position.applyAxisAngle(new THREE.Vector3(1,0,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(1,0,0),k*Math.PI/2);
            }
        }

    }

    r(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.x,1) || this.nearTo(position.x,0)){
                position.applyAxisAngle(new THREE.Vector3(1,0,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(1,0,0),k*Math.PI/2);
            }
        }

    }

    x(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            position.applyAxisAngle(new THREE.Vector3(1,0,0),k*Math.PI/2);
            mesh.position.copy(position);
            mesh.rotateOnWorldAxis(new THREE.Vector3(1,0,0),k*Math.PI/2);

        }

    }

    U(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.y,1)){
                position.applyAxisAngle(new THREE.Vector3(0,1,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,1,0),k*Math.PI/2);
            }
        }

    }

    u(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.y,1) || this.nearTo(position.y,0)){
                position.applyAxisAngle(new THREE.Vector3(0,1,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,1,0),k*Math.PI/2);
            }
        }

    }

    y(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            position.applyAxisAngle(new THREE.Vector3(0,1,0),k*Math.PI/2);
            mesh.position.copy(position);
            mesh.rotateOnWorldAxis(new THREE.Vector3(0,1,0),k*Math.PI/2);
        }

    }

    F(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.z,1)){
                position.applyAxisAngle(new THREE.Vector3(0,0,1),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,0,1),k*Math.PI/2);
            }
        }

    }

    f(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.z,1) || this.nearTo(position.z,0)){
                position.applyAxisAngle(new THREE.Vector3(0,0,1),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,0,1),k*Math.PI/2);
            }
        }

    }

    z(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            position.applyAxisAngle(new THREE.Vector3(0,0,1),k*Math.PI/2);
            mesh.position.copy(position);
            mesh.rotateOnWorldAxis(new THREE.Vector3(0,0,1),k*Math.PI/2);
        }

    }

    L(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.x,-1)){
                position.applyAxisAngle(new THREE.Vector3(1,0,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(1,0,0),k*Math.PI/2);
            }
        }

    }

    l(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.x,-1) || this.nearTo(position.x,0)){
                position.applyAxisAngle(new THREE.Vector3(1,0,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(1,0,0),k*Math.PI/2);
            }
        }

    }

    D(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.y,-1)){
                position.applyAxisAngle(new THREE.Vector3(0,1,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,1,0),k*Math.PI/2);
            }
        }

    }

    d(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.y,-1) || this.nearTo(position.y,0)){
                position.applyAxisAngle(new THREE.Vector3(0,1,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,1,0),k*Math.PI/2);
            }
        }

    }

    B(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.z,-1)){
                position.applyAxisAngle(new THREE.Vector3(0,0,1),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,0,1),k*Math.PI/2);
            }
        }

    }

    b(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.z,-1) || this.nearTo(position.z,0)){
                position.applyAxisAngle(new THREE.Vector3(0,0,1),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,0,1),k*Math.PI/2);
            }
        }

    }

    M(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.x,0)){
                position.applyAxisAngle(new THREE.Vector3(1,0,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(1,0,0),k*Math.PI/2);
            }
        }

    }

    E(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        cw = !cw;
        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.y,0)){
                position.applyAxisAngle(new THREE.Vector3(0,1,0),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,1,0),k*Math.PI/2);
            }
        }

    }

    S(clockwise){
        let cw = clockwise;
        if(cw === undefined){
            cw = true;
        }

        let k = cw?-1:1;
        for(let i=0; i<this.blocks.length; i++){
            let mesh = this.blocks[i].mesh;
            let position = mesh.getWorldPosition();
            this.fixPosition(position);
            if(this.nearTo(position.z,0)){
                position.applyAxisAngle(new THREE.Vector3(0,0,1),k*Math.PI/2);
                mesh.position.copy(position);
                mesh.rotateOnWorldAxis(new THREE.Vector3(0,0,1),k*Math.PI/2);
            }
        }

    }

    upset(count){
        let turns = count || 100;
        for(let i=0; i<turns; i++){
            this.randomTurn();
        }
    }

    randomTurn(){
        let opt = Math.floor(Math.random()*6);
        let clockwise = Math.random()*2>1?true:false;
        switch (opt){
            case 0: this.R(clockwise); break;
            case 1: this.U(clockwise); break;
            case 2: this.F(clockwise); break;
            case 3: this.L(clockwise); break;
            case 4: this.D(clockwise); break;
            case 5: this.B(clockwise); break;
        }
    }

    getBlock(colorList){
        for(let i=0; i<this.blocks.length; i++){
            let block = this.blocks[i];
            let mesh = block.mesh;
            if(mesh.children.length !== colorList.length){
                continue;
            }

            let planeList = [];
            for(let k=0; k<colorList.length; k++){
                let plane = block.hasColor(colorList[k]);
                if(!plane){
                    break;
                }
                planeList.push(plane);
            }

            if(planeList.length !== colorList.length){
                continue;
            }

            //找到了

            let position = mesh.getWorldPosition();
            this.fixPosition(position);


            let direction = planeList[0].getWorldDirection();
            direction.x = Math.round(direction.x);
            direction.y = Math.round(direction.y);
            direction.z = Math.round(direction.z);

            return {
                position: position,
                direction: direction
            };

        }

        return null;
    }

    getPositionColor(x,y,z){
        for(let i=0; i<this.blocks.length; i++){
            let block = this.blocks[i];

            let color = block.getPositionColor(new THREE.Vector3(x,y,z));
            if(color){
                return color;
            }
        }

        return null;
    }

    restore() {
        // alert('能力有限，告辞');

        //中心放对
        let blockObj = this.getBlock(['white']);
        let position = blockObj.position;
        let direction = blockObj.direction;
        if(direction.y === 1){
            this.x();
            this.x();
        }else if(direction.x === 1){
            this.z();
        }else if(direction.x === -1){
            this.z(false);
        }else if(direction.z === 1){
            this.x(false);
        }else if(direction.z === -1){
            this.x();
        }

        blockObj = this.getBlock(['red']);
        position = blockObj.position;
        direction = blockObj.direction;
        if(direction.x === 1){
            this.y();
        }else if(direction.x === -1){
            this.y(false);
        }else if(direction.z === -1){
            this.y();
            this.y();
        }

        this.cross();
    }

    //底层十字
    cross(){

        //白红
        let blockObj = this.getBlock(['white','red']);
        let position = blockObj.position;
        let direction = blockObj.direction;

        if(direction.y === 1){
            if(position.x === 1){
                this.R();
                this.R();
            }else if(position.x === -1){
                this.L();
                this.L();
            }else if(position.z === 1){
                this.F();
                this.F();
            }else if(position.z === -1){
                this.B();
                this.B();
            }
        }else if(direction.z === 1){
            if(position.y !== 0){
                this.F();
            }

            blockObj = this.getBlock(['white','red']);
            position = blockObj.position;
            direction = blockObj.direction;

            if(position.x === 1){
                this.R(false);
            }else if(position.x === -1){
                this.L();
            }

        }else if(direction.z === -1){
            if(position.y !== 0){
                this.B();
            }

            blockObj = this.getBlock(['white','red']);
            position = blockObj.position;
            direction = blockObj.direction;

            if(position.x === 1){
                this.R();
            }else if(position.x === -1){
                this.L(false);
            }

        }else if(direction.x === 1){
            if(position.y !== 0){
                this.R();
            }

            blockObj = this.getBlock(['white','red']);
            position = blockObj.position;
            direction = blockObj.direction;

            if(position.z === 1){
                this.F();
            }else if(position.z === -1){
                this.B(false);
            }

        }else if(direction.x === -1){
            if(position.y !== 0){
                this.L();
            }

            blockObj = this.getBlock(['white','red']);
            position = blockObj.position;
            direction = blockObj.direction;

            if(position.z === 1){
                this.F(false);
            }else if(position.z === -1){
                this.B();
            }

        }

        //到了底面
        blockObj = this.getBlock(['white','red']);
        position = blockObj.position;
        direction = blockObj.direction;
        if(position.x === 1){
            this.D(false);
        }else if(position.x === -1){
            this.D();
        }else if(position.z === -1){
            this.D();
            this.D();
        }

    }

}